package org.hxzon.cyclone;

//random.h,random.cpp
public class Random {

    // Internal mechanics
    private int p1, p2;
    //unsigned 
    private long[] buffer = new long[17];

    /**
     * left bitwise rotation
     */
    //unsigned,unsigned,unsigned
    public long rotl(long n, long r) {
        return (n << r) | (n >> (32 - r));
    }

    /**
     * right bitwise rotation
     */
    //unsigned,unsigned,unsigned
    public long rotr(long n, long r) {
        return (n >> r) | (n << (32 - r));
    }

    /**
     * Creates a new random number stream with a seed based on
     * timing data.
     */
    public Random() {
        this(0);
    }

    /**
     * Creates a new random stream with the given seed.
     */
    //unsigned
    public Random(long seed) {
        seed(seed);
    }

    /**
     * Sets the seed value for the random stream.
     */
    //unsigned
    public void seed(long seed) {
        if (seed == 0) {
            //seed = (unsigned) clock();
            seed = System.currentTimeMillis();
        }

        // Fill the buffer with some basic random numbers
        //unsigned i
        for (int i = 0; i < 17; i++) {
            // Simple linear congruential generator
            seed = seed * 2891336453L + 1;
            buffer[i] = seed;
        }

        // Initialize pointers into the buffer
        p1 = 0;
        p2 = 10;
    }

    /**
     * Returns the next random bitstring from the stream. This is
     * the fastest method.
     */
    //unsigned 
    public long randomBits() {
        // Rotate the buffer and store it back to itself
        //unsigned 
        long result = buffer[p1] = rotl(buffer[p2], 13) + rotl(buffer[p1], 9);

        // Rotate pointers
        if (--p1 < 0)
            p1 = 16;
        if (--p2 < 0)
            p2 = 16;

        // Return result
        return result;
    }

    /**
     * Returns a random floating point number between 0 and 1.
     */
    //单精度版
    public double randomReal() {
        // Get the random number
        //unsigned 
        long bits = randomBits();

        // Set up a reinterpret structure for manipulation
//            union {
//                real value;
//                unsigned word;
//            } convert;
        double convert;

        // Now assign the bits to the word. This works by fixing the ieee
        // sign and exponent bits (so that the size of the result is 1-2)
        // and using the bits to create the fraction part of the float.
        //convert.word = (bits >> 9) | 0x3f800000;
        convert = (bits >> 9) | 0x3f800000;

        // And return the value
        //return convert.value - 1.0f;
        return convert - 1.0f;
    }

    //双精度版
//        real Random::randomReal()
//        {
//            // Get the random number
//            unsigned bits = randomBits();
//
//            // Set up a reinterpret structure for manipulation
//            union {
//                real value;
//                unsigned words[2];
//            } convert;
//
//            // Now assign the bits to the words. This works by fixing the ieee
//            // sign and exponent bits (so that the size of the result is 1-2)
//            // and using the bits to create the fraction part of the float. Note
//            // that bits are used more than once in this process.
//            convert.words[0] =  bits << 20; // Fill in the top 16 bits
//            convert.words[1] = (bits >> 12) | 0x3FF00000; // And the bottom 20
//
//            // And return the value
//            return convert.value - 1.0;
//        }

    /**
     * Returns a random floating point number between 0 and scale.
     */
    public double randomReal(double scale) {
        return randomReal() * scale;
    }

    /**
     * Returns a random floating point number between min and max.
     */
    public double randomReal(double min, double max) {
        return randomReal() * (max - min) + min;
    }

    /**
     * Returns a random integer less than the given value.
     */
    //unsigned,unsigned
    public long randomInt(long max) {
        return randomBits() % max;
    }

    /**
     * Returns a random binomially distributed number between -scale
     * and +scale.
     */
    public double randomBinomial(double scale) {
        return (randomReal() - randomReal()) * scale;
    }

    /**
     * Returns a random vector where each component is binomially
     * distributed in the range (-scale to scale) [mean = 0.0f].
     */
    public Vector3 randomVector(double scale) {
        return new Vector3(randomBinomial(scale), randomBinomial(scale), randomBinomial(scale));
    }

    /**
     * Returns a random vector where each component is binomially
     * distributed in the range (-scale to scale) [mean = 0.0f],
     * where scale is the corresponding component of the given
     * vector.
     */
    public Vector3 randomVector(Vector3 scale) {
        return new Vector3(randomBinomial(scale.x), randomBinomial(scale.y), randomBinomial(scale.z));
    }

    /**
     * Returns a random vector in the cube defined by the given
     * minimum and maximum vectors. The probability is uniformly
     * distributed in this region.
     */
    public Vector3 randomVector(Vector3 min, Vector3 max) {
        return new Vector3(randomReal(min.x, max.x), randomReal(min.y, max.y), randomReal(min.z, max.z));
    }

    /**
     * Returns a random vector where each component is binomially
     * distributed in the range (-scale to scale) [mean = 0.0f],
     * except the y coordinate which is zero.
     */
    public Vector3 randomXZVector(double scale) {
        return new Vector3(randomBinomial(scale), 0, randomBinomial(scale));
    }

    /**
     * Returns a random orientation (i.e. normalized) quaternion.
     */
    public Quaternion randomQuaternion() {
        Quaternion q = new Quaternion(randomReal(), randomReal(), randomReal(), randomReal());
        q.normalise();
        return q;
    }

}
